#include "../include/buffer.h"
#include "../include/defs.h"
#include "../include/kc.h"
#include "../include/video.h"
#include "../include/terminal.h"
#include "../include/process.h"
#include "../include/scheduler.h"
#include "../include/syscalls.h"
#include "../include/sleep.h"
#include "../include/kasm.h"
#include "../include/fileSystem/directory.h"
#include "../include/fileSystem/file.h"
#include "../include/fileSystem/fileDescriptor.h"

extern tty ttyArray[]; //array con todas las terminales
extern int curTTY; //numero de la terminal actual

extern process_t processes[];	//lista de procesos
extern int currProc;			//proceso actual

//SYSTEM CALLS----------------------------------------------------------------
//funcion de escritura de bajo nivel, llama a las funciones de video
int syswrite(int fd, int cant, int flags, char * buffer, char * arg5);
//funcion de lectura de bajo nivel
int sysread(int fd, int cant, int flags, char * buffer, char * arg5);
//funcion de bajo nivel que cede el procesador
int sysyield(int arg1, int arg2, int arg3, char * arg4, char * arg5);
//funcion de bajo nivel que crea un nuevo proceso
int sysstartProcess(int priority, int isBackground, int argc,
		char * argv, char * processAux);
int syswait(int pid, int arg2, int arg3, char * arg4, char * arg5);
//funcion de bajo nivel que mata procesos en cascada
int syskill(int pid, int arg2, int arg3, char * arg4, char * arg5);
//funcion de bajo nivel que muestra los procesos
int sysgetproc(int arg1, int arg2, int arg3, char * processes, char * arg5);
//DEBUG para llamar cualquier cosa que un proceso no pueda de forma rapida
int sysdebug(int arg1, int arg2, int arg3, char * arg4, char * arg5);
//estas funciones setean y buscan la posicion x e y actual de la video
int sysvideoXY(int videoOp, int x, int y, char * arg4, char * arg5);
//hace clear del buffer de video
int sysvideoClear(int arg1, int arg2, int arg3, char * arg4, char * arg5);
//muestra o esconde el cursor (cursorOp = 0 lo esconde)
int sysvideoCursor(int cursorOp, int arg2, int arg3, char * arg4, char * arg5);
//cambia el atributo de la video por attr
int sysvideoAttr(int attr, int arg2, int arg3, char * arg4, char * arg5);
//hace dormir al proceso que hace el call durante 'segundos' segundos
int syssleep(int segundos, int arg2, int arg3, char * arg4, char * arg5);
//trata de crear un nuevo directorio
int sysmkdir(int mode, int arg2, int arg3, char * pathName, char * arg5);
//trata de abrir un nuevo directorio
int sysopendir(int arg1, int arg2, int arg3, char * pathName, char * arg5);
//cierra el directorio referenciado por el file descriptor fd
int sysclosedir(int fd, int arg2, int arg3, char * arg4, char * arg5);
//trata de leer un directorio, deja en dirEnt la entrada leida
int sysreaddir(int fd, int arg2, int arg3, char * dirEnt, char * arg5);
//trata de cambiar el directorio actual del proceso
int syschdir(int arg1, int arg2, int arg3, char * pathName, char * arg5);
//deja en name le path del directorio actual
int sysgetcwd(int size, int arg2, int arg3, char * name, char * arg5);
//crea o abre el archivo que se encuentra en pathName
int sysopen(int mode, int arg2, int arg3, char * pathName, char * arg5);
//cierra el archivo referenciado por el file descriptor fd
int sysclose(int fd, int arg2, int arg3, char * arg4, char * arg5);
//cambia el nombre del archivo o directorio en oldPath por el de newPath.
int sysrename(int arg1, int arg2, int arg3, char * oldPath, char * newPath);
//trata de borrar un archivo o directorio, segun opNum
int sysunlink(int opNum, int arg2, int arg3, char * path, char * arg5);
//devuelve el tamño del archivo o directorio
int sysgetFileSize(int arg1, int arg2, int arg3, char * path, char * arg5);
//----------------------------------------------------------------------------


//tipo de funcion de system call
typedef int (*syscallF)(int, int, int, char *, char *);

//array con todos los system call (las funciones deben estar en el mismo
//orden que el enum syscallOpNum definido en syscalls.h
syscallF syscallList[CANT_SYSCALLS] = {sysread, syswrite, sysyield,
	sysstartProcess, syswait, syskill, sysgetproc, sysdebug, sysvideoXY,
	sysvideoClear, sysvideoCursor, sysvideoAttr, syssleep, sysmkdir, sysopendir,
	sysclosedir, sysreaddir, syschdir, sysgetcwd, sysopen, sysclose, sysrename,
	sysunlink, sysgetFileSize};


//handler de los system call
int
int_80(syscallOpNum opNum, int arg1, int arg2, int arg3, char * arg4, char * arg5){
	int resp = 0;

	if (opNum >= 0 && opNum < CANT_SYSCALLS)
		resp = syscallList[opNum](arg1, arg2, arg3, arg4, arg5);

	return resp;
}

//Funcion de escritura de bajo nivel, llama a las funciones de video
int
syswrite(int fd, int cant, int flags, char * buffer, char * arg5) {
	//me fijo si el fd no es stdin, stdout, o stderr
	if (fd != FD_STDIN && fd != FD_STDOUT && fd != FD_STDERR ) {
		//si el numero esta en el rango posible del proceso, llamo a readImpl
		if (fd < FD_PER_PROC) {
			return writeImpl(fd, buffer, cant);
		}
		//por default voy a los puertos. no funciona para puertos menores a 10
		_outport(fd, *buffer);
		return cant;
	}

	//terminal del proceso actual
	int currProcTTY = processes[currProc].tty;

	if (!processes[currProc].background) {
		//llamo a la funcion de video
		screen_write(&ttyArray[currProcTTY].stdout, buffer, cant);

		//hago flush del buffer solo si el proceso es de la terminal actual
		if (currProcTTY == curTTY)
			flush_screen(&(ttyArray[curTTY].stdout));
	}

	return cant;
}

//Funcion de lectura de bajo nivel
int
sysread(int fd, int cant, int flags, char * buffer, char * arg5) {

	//me fijo si el fd no es stdin, stdout, o stderr
	if (fd != FD_STDIN && fd != FD_STDOUT && fd != FD_STDERR ) {
		//si el numero esta en el rango posible del proceso, llamo a readImpl
		if (fd < FD_PER_PROC) {
			return readImpl(fd, buffer, cant);
		}
		//por default voy a los puertos. no funciona para puertos menores a 10
		_inport(fd);
		return *buffer;
	}

	//terminal del proceso actual
	int currProcTTY = processes[currProc].tty;

	//si el buffer de lectura esta vacio, me bloqueo y sedo el procesador,
	// a menos que este setado el flag de no bloqueante
	if ( !(flags & O_NONBLOCK) && buffisempty(&ttyArray[currProcTTY].stdin) ) {
		blockProcess(currProc, B_READ); /*toque*/
		_sys_yield();
	}

	if (!processes[currProc].background)
		return getcharbuff(&ttyArray[currProcTTY].stdin, buffer, cant);

	return 0;
}

int
sysyield(int fd, int cant, int arg3, char * buffer, char * arg5) {
	_sys_yield();
	return 1;
}

int
sysstartProcess(int priority, int isBackground, int argc,
	char * argv, char * processAux){

	//chequeo que me pasen un valor de isBackground correcto, sino por default
	//lo mando al backGround
	if (isBackground != 0 && isBackground != 1)
		isBackground = 1;

	//chequeo que me pasen un valor de argc correcto, o salgo con error
	if (argc < 0 || argc > 6) {
		return -1;
	}

	//chequeo que me pasen un valor de priority correcto, sino pongo la
	//prioridad minima: CANT_PRIO
	if (priority < 0 || priority > CANT_PRIO) {
		priority = CANT_PRIO;
	}

	return createProcess(((stProcessAux*)processAux)->procName,
		((stProcessAux*)processAux)->process, isBackground, argc,
		(char**)argv, priority, processes[currProc].tty, 0x2000);
}

int
syswait(int pid, int arg2, int arg3, char * arg4, char * arg5){
	return waitImpl(pid);
}

int
syskill(int pid, int arg2, int arg3, char * buffer, char * arg5){
	return killProc(pid);
}

int
sysgetproc(int maxProcs, int arg2, int arg3, char * processes, char * arg5){
	return schedulerGetProcs((process_info_t *)processes, maxProcs);
}

int
sysdebug(int arg1, int arg2, int arg3, char * arg4, char * arg5) {
	return 0;
}

int
sysvideoXY(int videoOp, int x, int y, char * arg4, char * arg5) {
	//terminal del proceso actual
	int currProcTTY = processes[currProc].tty;
	if (videoOp == 0) {
		screen_get_pos(&ttyArray[currProcTTY].stdout, (int*)x, (int*)y);
	}
	else {
		if (!processes[currProc].background) {
			screen_set_pos(&ttyArray[currProcTTY].stdout, x, y);
		}
	}

	return 1;
}

int
sysvideoClear(int arg1, int arg2, int arg3, char * arg4, char * arg5) {
	//terminal del proceso actual
	int currProcTTY = processes[currProc].tty;

	if (!processes[currProc].background) {
		scrollFullScreen(&ttyArray[currProcTTY].stdout);
		//hago flush del buffer solo si el proceso es de la terminal actual
		if (currProcTTY == curTTY) {
			flush_screen(&(ttyArray[curTTY].stdout));
			screen_set_pos(&ttyArray[currProcTTY].stdout, 0, 0);
		}
	}
	return 1;
}

int
sysvideoCursor(int cursorOp, int tty, int arg3, char * arg4, char * arg5) {
	//terminal del proceso
	int currProcTTY = processes[currProc].tty;
	if (!processes[currProc].background) {
		if (cursorOp == 0) {
			show_hide_cursor(&ttyArray[currProcTTY].stdout, 0);
		}
		else {
			show_hide_cursor(&ttyArray[currProcTTY].stdout, 1);
		}
	}

	return 1;
}

int
sysvideoAttr(int attr, int arg2, int arg3, char * arg4, char * arg5) {
	int currProcTTY = processes[currProc].tty;
	if (!processes[currProc].background) {
		changeCurAttr(&ttyArray[currProcTTY].stdout, attr);
	}

	return 1;
}

int
syssleep(int segundos, int arg2, int arg3, char * arg4, char * arg5) {
	return sleepImpl(segundos);
}

int
sysmkdir(int mode, int arg2, int arg3, char * pathName, char * arg5) {
	return mkdirImpl(pathName, mode);
}

int
sysopendir(int arg1, int arg2, int arg3, char * pathName, char * arg5) {
	return opendirImpl(pathName);
}

int
sysclosedir(int fd, int arg2, int arg3, char * arg4, char * arg5) {
	closedirImpl(fd);
	return 1;
}

int
sysreaddir(int fd, int arg2, int arg3, char * dirEnt, char * arg5) {
	return readdirImpl(fd, (dirEntry *) dirEnt);
}

int
syschdir(int arg1, int arg2, int arg3, char * pathName, char * arg5) {
	return chdirImpl(pathName);
}

int
sysgetcwd(int size, int arg2, int arg3, char * name, char * arg5) {
	return (int)getcwdImpl(name, size);
}

int
sysopen(int mode, int arg2, int arg3, char * pathName, char * arg5) {
	return openImpl(pathName, mode);
}

int
sysclose(int fd, int arg2, int arg3, char * arg4, char * arg5) {
	closeImpl(fd);
	return 1;
}

int
sysrename(int arg1, int arg2, int arg3, char * oldPath, char * newPath) {
	return renameImpl(oldPath, newPath);
}

int
sysunlink(int opNum, int arg2, int arg3, char * path, char * arg5) {
	if (opNum == 0)
		return rmdirImpl(path);
	else
		return unlinkImpl(path);
}

int
sysgetFileSize(int arg1, int arg2, int arg3, char * path, char * arg5) {
	return getFileSizeImpl(path);
}

